/**
 * Copyright 2018 lenos
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.jantd.modules.activiti.controller;

import cn.jantd.core.api.vo.Result;
import cn.jantd.modules.activiti.config.ActivitiConfig;
import cn.jantd.modules.activiti.entity.*;
import cn.jantd.modules.activiti.service.IActRuExecutionService;
import cn.jantd.modules.activiti.service.IActRuModalService;
import cn.jantd.modules.activiti.entity.ActModel;
import cn.jantd.modules.activiti.entity.ProcessDefinition;
import cn.jantd.modules.activiti.entity.TaskFileType;
import cn.jantd.modules.activiti.service.ITaskFileTypeService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.activiti.bpmn.converter.BpmnXMLConverter;
import org.activiti.bpmn.model.*;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.impl.persistence.entity.DeploymentEntity;
import org.activiti.engine.impl.persistence.entity.ModelEntity;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.sql.Wrapper;
import java.util.*;

/**
 * @author zhuxiaomeng
 * @date 2018/1/13.
 * @email 154040976@qq.com
 * <p>
 * 流程管理 流程创建、发布 流程节点绑定角色/用户(绑定用户 开始ing)
 */
@Controller
@RequestMapping(value = "/act/model/")
public class ModelController {

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private ITaskFileTypeService iTaskFileTypeService;

    @Autowired
    private IActRuModalService iActRuModalService;

    @Autowired
    private IActRuExecutionService actRuExecutionService;

    /**
     * 模型列表
     */
    @ResponseBody
    @GetMapping(value = "list")
    public Object showModel(
            @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
            @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, ActModel actModel) {
        Result<IPage<ActModel>> result = new Result<IPage<ActModel>>();
        ModelQuery modelQuery = repositoryService.createModelQuery();
        if (actModel != null) {
            if (!StringUtils.isEmpty(actModel.getKey())) {
                modelQuery.modelKey(actModel.getKey());
            }
            if (!StringUtils.isEmpty(actModel.getName())) {
                modelQuery.modelNameLike("%" + actModel.getName() + "%");
            }
        }
        List<Model> models = modelQuery.listPage(Integer.valueOf(pageSize) * (Integer.valueOf(pageNo) - 1), Integer.valueOf(pageSize));
        long count = repositoryService.createModelQuery().count();
        List<ActModel> list = new ArrayList<>();
        models.forEach(mo -> list.add(new ActModel(mo)));
        List<ActRuExecution> actRuExecutionList = actRuExecutionService.list();
        for (int i = 0; i < actRuExecutionList.size(); i++){
            for (int y = 0; y < list.size(); y++) {
                 if(actRuExecutionList.get(i).getProcDefId().indexOf(list.get(y).getKey()) != -1){
                     list.get(y).setRefModal("1");
                 }else {
                     list.get(y).setRefModal("0");
                 }
            }

        }
        result.setResult(new Page<ActModel>().setTotal(count).setRecords(list));
        return result;
    }

    @ResponseBody
    @PostMapping(value = "add")
    public Object addModel(@RequestBody JSONObject json) throws IOException {
        ModelEntity model = objectMapper.readValue(json.toString(), ModelEntity.class);
        model.setKey("A" + UUID.randomUUID().toString());
        model.setCategory("model_task");
        model.setMetaInfo(String.format("{\"description\":\"%s\",\"company\":\"%s\"}", json.getString("description"), json.getString("company")));
        model.setId(null);
        repositoryService.saveModel(model);

        String id = model.getId();
        String content = json.getString("xml");
        content = content.replaceAll("ID_Process_Key", model.getKey());
        content = content.replaceAll("NAME_Process_Key", model.getName());
        content = content.replaceAll("CATEGORY_Process_Key", model.getCategory());
        repositoryService.addModelEditorSource(id, content.getBytes(ActivitiConfig.source_charset));

        //任务节点输入输出格式保存:w
        JSONArray node = json.getJSONArray("node");
        System.out.println("看看：" + node.toJSONString());


        for (int i = 0; i < node.size(); i++) {
            HashMap hashMap = (LinkedHashMap) node.get(i);
            hashMap.put("modelId", id);
            String jsonStr = JSON.toJSONString(node.get(i));
            TaskFileType taskFiletype = objectMapper.readValue(jsonStr, TaskFileType.class);
            iTaskFileTypeService.save(taskFiletype);
        }
        Result result = new Result();
        result.setResult(new ActModel(model));
        return result;
    }


    @ResponseBody
    @DeleteMapping(value = "delete")
    public Object deleteModel(String id) {
        Result result = new Result();
        repositoryService.deleteModel(id);
        LambdaQueryWrapper<TaskFileType> taskFileTypeLambdaQueryWrapper = new LambdaQueryWrapper<>();
        taskFileTypeLambdaQueryWrapper.eq(TaskFileType::getModelId, id);
        iTaskFileTypeService.remove(taskFileTypeLambdaQueryWrapper);
        result.setMessage("删除成功");
        return result;
    }

    @ResponseBody
    @PutMapping(value = "edit")
    public Object editModel(@RequestBody JSONObject json) throws IOException {
        Model model = objectMapper.readValue(json.toString(), ModelEntity.class);
        Result result = (Result) this.deleteModel(model.getId());

        //删除任务文件类型
        QueryWrapper<TaskFileType> taskFileTypeQueryWrapper = new QueryWrapper<>();
        taskFileTypeQueryWrapper
                .eq("model_id", model.getId());
        iTaskFileTypeService.remove(taskFileTypeQueryWrapper);

        if (result.isSuccess()) {
            result = (Result) this.addModel(json);
        }
        return result;
    }


    @ResponseBody
    @GetMapping("source/{modelId}")      //参数为modelId
    public Object getModelSource(@PathVariable("modelId") String modelId) throws UnsupportedEncodingException {
        byte[] bytes = repositoryService.getModelEditorSource(modelId);
        System.out.println("看看byte是什么：" + bytes);
        Result result = new Result<>();
        if (null != bytes) {
            result.setResult(new String(bytes, ActivitiConfig.source_charset));
        }
        return result;
    }

    @ResponseBody
    @PostMapping(value = "deploy/{modelId}")
    public Result deploy(@PathVariable("modelId") String modelId) throws IOException, XMLStreamException {
        Result result = new Result();
        Model model = repositoryService.getModel(modelId);
        byte[] bytes = repositoryService.getModelEditorSource(model.getId());

        if (bytes == null) {
            return Result.error("模型为空");
        }
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(XMLInputFactory.newInstance().createXMLStreamReader((new ByteArrayInputStream(bytes))));

        if (bpmnModel.getProcesses().size() == 0) {
            return Result.error("数据不符合要求!");
        }

        List list = (ArrayList) bpmnModel.getProcesses().get(0).getFlowElements();

        for (int i = 0; i < list.size(); i++) {
            if (list.get(i) instanceof StartEvent) {
                if (((StartEvent) list.get(i)).getOutgoingFlows().size() < 1) {
                    return Result.error("开始节点连线没有连线!");
                }
            } else if (list.get(i) instanceof UserTask) {
                if (((UserTask) list.get(i)).getName() == null || ((UserTask) list.get(i)).getName().length() < 0) {
                    return Result.error("请检查任务节点是否命名!");
                } else if (((UserTask) list.get(i)).getOutgoingFlows().size() < 1) {
                    return Result.error("请检查" + ((UserTask) list.get(i)).getName() + "节点输出连线是否正确!");
                } else if (((UserTask) list.get(i)).getIncomingFlows().size() < 1) {
                    return Result.error("请检查" + ((UserTask) list.get(i)).getName() + "节点输入连线是否正确!");
                }
            } else if (list.get(i) instanceof EndEvent) {
                if (((EndEvent) list.get(i)).getIncomingFlows().size() < 1) {
                    return Result.error("结束节点连线不正确!");
                }
            }
        }

        List plist = new ArrayList();
        for (int i = 0; i < list.size(); i++) {
            if (list.get(i) instanceof UserTask) {
                plist.add(list.get(i));
            }
        }
        for (int i = 0; i < plist.size(); i++) {
            TaskFileType taskFileType = new TaskFileType();
            if (((UserTask) plist.get(i)).getAttributes().size() < 1) {
                return Result.error(((UserTask) plist.get(i)).getName() + "任务节点没有定义上传文件格式!");
            } else if (((ExtensionAttribute) ((List) ((HashMap) (((UserTask) plist.get(i)).getAttributes())).get("outputFormat")).get(0)).getValue() == "") {
                return Result.error(((UserTask) plist.get(i)).getName() + "任务节点定义上传文件格式不符合规范!");
            }
            taskFileType.setOutputFormat(((ExtensionAttribute) ((List) ((HashMap) (((UserTask) plist.get(i)).getAttributes())).get("outputFormat")).get(0)).getValue());

            taskFileType.setActivityId(((UserTask) plist.get(i)).getId());
            taskFileType.setModelId(modelId);
            iTaskFileTypeService.save(taskFileType);
        }
//        JsonNode modelNode = new ObjectMapper().readTree(bytes);
//        CustomBpmnJsonConverter.getConvertersToBpmnMap().put("UserTask", CustomUserTaskJsonConverter.class);
//        BpmnModel bpmnModel = new BpmnJsonConverter().convertToBpmnModel(modelNode);


        //发布流程
        result.setResult(new ProcessDefinition(deployModelSource(model, new String(bytes, ActivitiConfig.source_charset))));
        result.setMessage("操作成功");
        return result;
    }

    @ResponseBody
    @PostMapping(value = "deploySource")
    public Result deploySource(@RequestBody JSONObject json) throws IOException, XMLStreamException {
        if (json == null || json.get("id") == null || json.get("source") == null) {
            return Result.error("数据不符合要求");
        }
        Result result = new Result();
        Model model = repositoryService.getModel(json.getString("id"));
        if (model == null) {
            return Result.error("数据不符合要求");
        }
        //人员分配是重新保存数据
        String id = model.getId();
        String content = json.getString("source");
        content = content.replaceAll("ID_Process_Key", model.getKey());
        content = content.replaceAll("NAME_Process_Key", model.getName());
        content = content.replaceAll("CATEGORY_Process_Key", model.getCategory());
        repositoryService.addModelEditorSource(id, content.getBytes(ActivitiConfig.source_charset));
        //任务节点输入输出格式保存:w
//        JSONArray nodes = json.getJSONArray("nodes");
//        List<TaskFileType> iTaskFileType = iTaskFileTypeService.getByModelId(model.getId());
//        for (int i = 0; i < iTaskFileType.size(); i++) {
//  /*          System.out.println("iTaskFileType:   "+iTaskFileType.get(i).getModelId());
//            List<TaskFileType> task1=iTaskFileTypeService.getByModel(iTaskFileType.get(i).getModelId());
//         *//*    for(int a=0;a<task1.size();a++){*//*
//            System.out.println("activiti1111111111111:"+task1.get(0).getActivityId());*/
//            for (int j = 0; j < nodes.size(); j++) {
//                HashMap hashMap = (LinkedHashMap) nodes.get(j);
//                hashMap.put("modelId", id);
//                String jsonStr = JSON.toJSONString(nodes.get(j));
//                TaskFileType taskFiletype = objectMapper.readValue(jsonStr, TaskFileType.class);
//
//                if (taskFiletype.getActivityId().equals(iTaskFileType.get(i).getActivityId())) {
//                    System.out.println("shujushishenme :     " + taskFiletype.getStartTime() + taskFiletype.getEndTime() + taskFiletype.getDateDiff());
//                    iTaskFileTypeService.updateByActiviti(taskFiletype);
//                }
//
//                /* if(taskFiletype.getActivityId().equals(task1.get(a).getActivityId())){*/
//
//                /*   }*/
//            }
//        }

        result.setResult(new ProcessDefinition(deployModelSource(model, json.getString("source"))));
        System.out.println("看看source数据:" + result);
        result.setMessage("操作成功");
        return result;
    }

    public org.activiti.engine.repository.ProcessDefinition deployModelSource(Model model, String source) throws UnsupportedEncodingException, XMLStreamException {
        source = source.replaceAll("userType=\"(assignee|candidateUsers|candidateGroups)\"", "");
        source = source.replaceAll("assignee=", "activiti:assignee=");
        source = source.replaceAll("candidateUsers=", "activiti:candidateUsers=");
        source = source.replaceAll("candidateGroups=", "activiti:candidateGroups=");
        source = source.replaceAll("teams=", "activiti:teams=");
        source = source.replaceAll("inputFormat=", "activiti:inputFormat=");
        source = source.replaceAll("outputFormat=", "activiti:outputFormat=");
        source = source.replaceAll("isTrusted=", "activiti:isTrusted=");
        source = source.replaceAll("description=", "activiti:description=");
        source = source.replaceAll("activityId=", "activiti:activityId=");
        source = source.replaceAll("activityId=", "activiti:activityId=");
        source = source.replaceAll("activityId=", "activiti:activityId=");
        source = source.replaceAll("taskStartTime=", "activiti:taskStartTime=");
        source = source.replaceAll("taskEndTime=", "activiti:taskEndTime=");
        source = source.replaceAll("dateDiff=", "activiti:dateDiff=");
        BpmnModel bpmnModel = new BpmnXMLConverter().convertToBpmnModel(XMLInputFactory.newInstance().createXMLStreamReader(new ByteArrayInputStream(source.getBytes(ActivitiConfig.source_charset))));
        if (bpmnModel.getProcesses().size() == 0) {
            throw new IllegalArgumentException("source is not legal bpmn xml!");
        }
        String processName = model.getName() + ".bpmn20.xml";
        DeploymentEntity deployment = (DeploymentEntity) repositoryService.createDeployment()
                .name(model.getName())
                .category(model.getCategory())
//                .addString(processName, source)
                .activateProcessDefinitionsOn(new Date())
                .addBpmnModel(processName, bpmnModel)
                .deploy();
        model.setDeploymentId(deployment.getId());
        org.activiti.engine.repository.ProcessDefinition definition = repositoryService.createProcessDefinitionQuery().deploymentId(deployment.getId()).latestVersion().singleResult();
        repositoryService.activateProcessDefinitionById(definition.getId());  //通过流程实例ID来激活流程实例
        repositoryService.saveModel(model);
//        存在模型定义id:w
        iTaskFileTypeService.updateDefinition(model.getId(), definition.getId());

        return definition;
    }

    /**
     * 已部署的模型列表
     */
    @ResponseBody
    @GetMapping(value = "deployed/list")
    public Object deployedList(
            @RequestParam(name = "pageNo", defaultValue = "1") Integer pageNo,
            @RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize, Model model) {
        Result<IPage<ActModel>> result = new Result<IPage<ActModel>>();
        ModelQuery deploymentQuery = repositoryService.createModelQuery().deployed().orderByLastUpdateTime().desc();
        if (model != null) {
            if (!StringUtils.isEmpty(model.getCategory())) {
                deploymentQuery.modelCategory(model.getCategory());
            }
            if (!StringUtils.isEmpty(model.getName())) {
                deploymentQuery.modelName("%" + model.getName() + "%");

            }
        }
        List<Model> list = deploymentQuery.listPage(Integer.valueOf(pageSize) * (Integer.valueOf(pageNo) - 1), Integer.valueOf(pageSize));

        List<ActModel> deploymentList = new ArrayList<>();
        list.forEach(mo -> deploymentList.add(new ActModel(mo)));
        long count = repositoryService.createDeploymentQuery().count();
        result.setSuccess(true);
        result.setResult(new Page<ActModel>().setRecords(deploymentList).setTotal(count));
        return result;
    }

    /**
     * 已部署的模型全部列表
     */
    @ResponseBody
    @GetMapping(value = "deployed/all")
    public Object deployedAllList(Model model) {
        Result<List<ActModel>> result = new Result<List<ActModel>>();
        ModelQuery deploymentQuery = repositoryService.createModelQuery().deployed().orderByLastUpdateTime().desc();
        if (model != null) {
            if (!StringUtils.isEmpty(model.getCategory())) {
                deploymentQuery.modelCategory(model.getCategory());
            }
            if (!StringUtils.isEmpty(model.getName())) {
                deploymentQuery.modelName("%" + model.getName() + "%");

            }
        }
        List<Model> list = deploymentQuery.list();

        List<ActModel> deploymentList = new ArrayList<>();
        list.forEach(mo -> deploymentList.add(new ActModel(mo)));
        result.setSuccess(true);
        result.setResult(deploymentList);
        System.out.println("看看部署的列表：" + result);
        return result;
    }

    /**
     * 启用流程模型
     */
    @ResponseBody
    @GetMapping(value = "process/activate/{processDefinitionId}")
    public Object activateProcess(@PathVariable("processDefinitionId") String processDefinitionId) {
        String tenantId = "";
        repositoryService.activateProcessDefinitionByKey(processDefinitionId, tenantId);
        Result<List> result = new Result<List>();
        return result;
    }

    /**
     * @Author: 风中的那朵云
     * @Description: 复制模板
     * @Date: 2020/5/6
     * @Version: 1.0
     */
    @ResponseBody
    @PostMapping(value = "copyTemplate")
    public Object copyTemplate(@RequestBody JSONObject json) throws IOException {
        ModelEntity model = objectMapper.readValue(json.toString(), ModelEntity.class);
        QueryWrapper<ActReModel> actRuModelQueryWrapper = new QueryWrapper<>();
        actRuModelQueryWrapper
                .eq("NAME_", model.getName()).orderByDesc();
        ActReModel actReModel = iActRuModalService.getOne(actRuModelQueryWrapper);
        if (actReModel != null) {
            Result result = new Result();
            result.setSuccess(false);
            result.setMessage("模型名称重复");
            return result;
        }
        model.setKey("A" + UUID.randomUUID().toString());   //随机的UUID
        model.setCategory("model_task");
        model.setMetaInfo(String.format("{\"description\":\"%s\",\"company\":\"%s\"}", json.getString("description"), json.getString("company")));
        model.setId(null);
        model.setDeploymentId(null);
        repositoryService.saveModel(model);

        String id = model.getId();
        String content = json.getString("xml");
        content = content.replaceAll("ID_Process_Key", model.getKey());
        content = content.replaceAll("NAME_Process_Key", model.getName());
        content = content.replaceAll("CATEGORY_Process_Key", model.getCategory());
        repositoryService.addModelEditorSource(id, content.getBytes(ActivitiConfig.source_charset));
        Result result = new Result();
        result.setResult(new ActModel(model));
        return result;
    }

    @ResponseBody
    @GetMapping("getTimes/{taskId}")
    public Object getTimes(@PathVariable String taskId) throws IOException {
        QueryWrapper<TaskFileType> taskFileTypeQueryWrapper = new QueryWrapper<>();
        taskFileTypeQueryWrapper
                .eq("activity_id", taskId).orderByDesc();
        TaskFileType taskFileType = iTaskFileTypeService.getOne(taskFileTypeQueryWrapper);
        Result result = new Result();
        result.setResult(taskFileType);
        return result;
    }

}
